The invention relates to multi-threaded computing environments, and more specifically, to a method and apparatus for synchronizing threads via selective object locking.
Multi-threaded computing environments allow different parts of a program, known as threads, to execute simultaneously. In recent years, multithreaded computing environments have become more popular because of the favorable performance characteristics provided by multithreaded applications.
Compared to the execution of processes in a multiprocessing environment, the execution of threads may be started and stopped very quickly because there is less runtime state to save and restore. The ability to quickly switch between threads can provide a relatively high level of data concurrency. In the context of a multi-threaded environment, data concurrency refers to the ability for multiple threads to concurrently access the same data. When the multi-threaded environment is a multi-processor system, each thread may be executed on a separate processor, thus allowing multiple threads to access shared data simultaneously.
Despite the favorable performance characteristics provided by multithreaded computing environments, they are not without their disadvantages. Specifically, in multithreaded applications, maintaining the integrity of data structures and variables can be particularly challenging since more than one thread can access the same data simultaneously. Unlike processes in multiprocessing environments, threads typically share a single address space and a set of global variables and are primarily distinguished by the value of their program counters and stack pointers. Consequently, the state of some commonly accessible data can be undergoing a change by one thread at the same time that it is being read by another thread, thus making the data unreliable.
To control access to common data by multiple threads, some systems synchronize or serialize the access to common data by threads. In the context of multithreaded environments, synchronizing threads refers to employing a mechanism to control the access to common data structures so that only one thread can access a common data structure at any given time. Typically, multiple requests to access common data are queued and then serviced one at a time, either by an operating system, multi-threaded system, or some other mechanism.
To synchronize threads, some programming languages include a programming language construct known as a xe2x80x9cmonitor.xe2x80x9d A monitor provides encapsulation of variables, methods and other types of data within an abstract data type. A monitor""s variables, methods and other data may only be accessed through a single access procedure, which ensures that only one thread may be actively accessing the monitor at any one time, with other access requests being queued until it is their turn. Other programming languages support a xe2x80x9csynchronizedxe2x80x9d statement which, when executed by a thread, effectively locks a block of code, so that only the thread which executed the synchronized statement can execute the locked block of code. Code which affects common data is then placed in a synchronized block of code so that only one thread may access the common data at any given time.
In object oriented environments thread synchronization is sometimes achieved through object locking. In general, object locking involves locking data associated with an object so that only the thread that invoked the methods associated with the object may cause changes to the data associated with the object. Because of the inherent reusability of object oriented languages, it is common for software developers to build large reusable object class libraries. Since library developers do not always know ahead of time how these object class libraries will be used, object locking is typically implemented for every object in the library.
Although object locking does prevent more than one thread from accessing data at the same time, in some circumstances object locking is unnecessary. For example, some applications employ only a single thread, which eliminates the concern that more than one thread will access object data simultaneously. Even for multithreaded applications, often data may be protected with thread safety mechanisms provided by an operating system or at a higher level by a multithreaded environment. In both of these situations, none of the objects in an object calling hierarchy need to be locked. Consequently, the time and system resources expended during the automatic execution of lock-related operations is wasted.
Invoking a high level object may cause large numbers of objects to be locked, since the invoked object may invoke another object, which itself may invoke another object. There is no theoretical limit to the depth of a calling hierarchy. Consequently, there is no limit to the time wasted by built-in lock operations when locking operations are not necessary. Locking an object can require significant system resources and time. For example, in some systems, obtaining a lock on an object requires that a first lock be obtained on a lock table and then a lock table entry constructed based upon information about the thread which is to obtain the lock.
Consider the object calling hierarchy 100 illustrated in FIG. 1. Dynamic calling hierarchy 100 includes object A 102, object B 104 and object C 106. Object A 102 includes data 108, such as variables and data structures associated with object A 102. Object A 102 also includes a method 110 which operates on data 108. Similarly, object B 104 includes data 112, such as variables and data structures associated with object B 104. Object B 104 also includes a method 114 which operates on data 112. Finally, as with object A 102 and object B 104, object C 106 includes data 116, such as variables and data structures associated with object C 106. Object C 106 also includes a method 118 which operates on data 116.
As illustrated by the object calling hierarchy 100 of FIG. 1, invoking method 110 of object A 102 causes method 114 of object B 104 to be invoked, which in turn causes method 118 of object C 106 to be invoked.
In a multithreaded environment, more than one thread (not illustrated) may invoke method 110, 114, 116 associated with objects 102, 104, 106, respectively. Furthermore, if these threads are unsynchronized, data 108, 112, 116 may be updated by more than one thread, making data 108, 112 and 116 unreliable.
According to a typical synchronization approach, when method 110 is invoked, the execution of code 120 causes a lock to be obtained on a lock object A 122, contained in data 108. Once lock object A 122 has been locked, only the thread which holds lock object A 122 can cause changes to occur to data 108. Specifically, since method 110 also includes code 123 for accessing data A 124 contained in data 108, only the thread which holds lock object A 122 can cause code 123 to be executed and cause changes to data A 124.
Invoking method 110 also causes the execution of code 125 which invokes method 114 of object B 104. Invoking method 114 causes a lock to be obtained on a lock object B 126 via the execution of code 128. Then, the execution of code 130 causes method 118 of object C 106 to be invoked. The execution of code 134 then causes a lock to be obtained on lock object C 132.
Method 118 includes code 135 which accesses data C 136 in data 116. Method 118 also includes code 137 which causes the release of the lock on lock object C 132. Control is then returned to method 114, where code 138 accesses data B 139 in data 112. Then the lock on lock object B 126 is released via the execution of code 140 and control is returned to method 110. Finally, the execution of code 142 causes the release of the lock on lock object A 122.
Although the built-in locking code 120, 142, 128, 140, 134, 137 ensures that only one thread will access data 108, 112, 116 at any given time, it requires three lock operations and three unlock operations. In some situations, locking and unlocking all three lock objects 122, 126, 132 is not necessary. For example, a single threaded application may invoke objects 102, 104, 106, which, by definition, means that only a single thread can cause changes to data 108, 112, 116. However, invoking methods associated with an object in an object calling hierarchy automatically causes locks to be obtained for all other invoked objects. This can use significant system resources, particularly for calling hierarchies containing many levels.
Consequently, in view of the need to protect data associated with objects in single threaded and multithreaded environments and the limitations in the prior approaches, an approach for synchronizing threads in a multithreaded environment which reduces the burden placed on system resources is highly desirable.
According to one aspect of the invention, a method is provided for protecting both first data associated with a first routine and second data associated with a second routine. The first routine causes a lock object to be locked to protect the first data. Then, the second routine causes the same lock object to be relocked to protect the second data while the lock object is locked to protect the first data.
According to another aspect of the invention, a method is provided for protecting data associated with a routine in a routine calling hierarchy. First, lock object data is provided to the routine. If the lock object data specifies a lock object, then the routine causes the specified lock object to be locked. On the other hand, if the lock object data specifies a predetermined value which indicates that no object locking is to be performed, then the routine does not cause any lock object to be locked.
According to another aspect of the invention, a computer system having a memory for protecting first data associated with a first routine and second data associated with a second routine is provided. The memory includes the first routine, the first data, the second routine, the second data and a lock object which can be locked by the first routine to protect the first data and which also can be relocked by the second routine to protect the second data while it is locked by the first routine to protect the first data.
According to another aspect of the invention, a computer data signal embodied in a carrier wave and representing a segment of instructions is provided for protecting both first data associated with a first routine and second data associated with a second routing. When executed by one or more processors, the sequence of instructions causes the first routine to cause a lock object to be locked to protect the first data. Then, the , second routine causes the same. lock object to be relocked to protect the second data while the lock object is locked to protect the first data.